package com.lits.file.service;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import javax.annotation.Resource;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.lits.file.common.UploadUtil;
import com.lits.file.config.LocalFileUploadCfg;

@Service
public class FileSvc {
	private static final Logger log = LoggerFactory.getLogger(FileSvc.class.getName());
	@Resource
	private LocalFileUploadCfg localFileUploadCfg;

	@Value("${file.max-upload-m:150}")
	private int maxUploadM;

	public FileSvc() throws NoSuchAlgorithmException {
	}

	/**
	 * 保存文件接口,如果文件名传null默认使用文件md5保存文件
	 *
	 * @param files        上传的文件列表
	 * @param destDir      目标文件夹
	 * @param hash         是否hash文件获取保存的目标文件名
	 * @param overwrite    是否覆盖已经存在的文件
	 * @return
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 */
	public String[] upload(MultipartFile[] files, String destDir, boolean hash, boolean overwrite)
			throws IOException, NoSuchAlgorithmException {
		if (files == null || files.length == 0 || StringUtils.isBlank(destDir)) {
			return new String[]{};
		}

		// 创建目标文件夹
		new File(destDir).mkdirs();

		// 写入目标文件
		int len = files.length;
		String[] res = new String[len];
		int read = 0;
		InputStream is = null;
		FileOutputStream fs = null;
		MessageDigest md5 = MessageDigest.getInstance("MD5");
		try {
			for (int i = 0; i < len; i++) {
				MultipartFile f = files[i];
				// TODO need check whether the multipart file exists in the disk
				// if exist, only need move operation other than write io
				// f.getResource().getFile().getAbsolutePath();
				is = f.getInputStream();
				if (is.available() > 0) {
					String fileNameFinal = f.getOriginalFilename();
					res[i] = fileNameFinal;
					String fdest = destDir + fileNameFinal;
					String fileType = UploadUtil.getFileType(fileNameFinal);
					// 检查上传文件是否存在
					File old = new File(fdest);
					if (old.exists()) {
						if (!overwrite) {
							// 非重写，不允许上传
							throw new IOException("旧版文件" + fdest + "已经存在");
						}
						if (is.available() > maxUploadM * 1048576L) {
							throw new IOException("旧版文件" + fdest + "已经存在, 且本次上传的文件大于" + maxUploadM + "M");
						}
					}
					fs = new FileOutputStream(fdest);
					byte[] buff = new byte[localFileUploadCfg.getReadBuffer()];
					while ((read = is.read(buff)) != -1) {
						fs.write(buff, 0, read);
						if (hash) {
							md5.update(buff, 0, read);
						}
					}
					fs.flush();
					fs.close();
					is.close();

					if (hash) {
						fileNameFinal = Hex.encodeHexString(md5.digest()) + fileType;
						UploadUtil.rename(fdest, fileNameFinal);

					}
					res[i] = fileNameFinal;

					log.info("upload file:{}, to {}", res[i], fileNameFinal);
				} else {
					res[i] = null;
				}
			}
		} finally {
			UploadUtil.closeStream(is);
			UploadUtil.closeStream(fs);
		}
		return res;

	}

	/**
	 * 高效的文件复制方法
	 * @param src
	 * @param dest
	 */
	private static void transferTo(String src, String dest){
			try {
				FileChannel readChannel = FileChannel.open(Paths.get(src), StandardOpenOption.READ);

				FileChannel writeChannel = FileChannel.open(Paths.get(dest), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

				readChannel.transferTo(0, readChannel.size(), writeChannel);

				readChannel.close();

				writeChannel.close();
			} catch (Exception e) {

				e.printStackTrace();

			}

	}

	public boolean deleteFile(String file) {
		File f = new File(file);
		if (f.exists()) {
			log.info("deleted file {}", file);
			return f.delete();
		}
		return false;
	}



	public static String calcHash(String path, Map<String, String> compareResult, MessageDigest md5) throws NoSuchAlgorithmException {

		int read = 0;
		InputStream is = null;
		try {
			is = new FileInputStream(path);
//			String fileNameOld = UploadUtil.getFileName(path);
			String fileType = UploadUtil.getFileType(path);

			byte[] buff = new byte[2048];
			while ((read = is.read(buff)) != -1) {
				md5.update(buff, 0, read);
			}

			String fileNameFinal = Hex.encodeHexString(md5.digest());
			md5.reset();
			if (compareResult.containsKey(fileNameFinal)) {
				log.info("duplicated file:{}, old:{}", path, compareResult.get(fileNameFinal));
				//Files.delete(Paths.get(path));
				//log.info("deleted:"+ path);
			} else {
				compareResult.put(fileNameFinal, path);
				String folder = path.substring(0, path.lastIndexOf("/")+1);
				System.out.println("folder of new:" + path);
				Path targetFile = Paths.get(folder + fileNameFinal + fileType);
				if(!Files.exists(targetFile)){
					Files.move(Paths.get(path), targetFile);
				}
			}
			return fileNameFinal;
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			UploadUtil.closeStream(is);
		}
		return null;
	}

	public static void getAllDuplicated(Path currentPath, Map<String, String> res, MessageDigest md5) throws IOException {

		Files.walkFileTree(currentPath, new SimpleFileVisitor<Path>(){

			public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
					throws IOException
			{
				String strFile = file.toAbsolutePath().toString();
				if(strFile.endsWith(".mp4")){
					try {
						calcHash(file.toAbsolutePath().toString(), res, md5);
					} catch (NoSuchAlgorithmException e) {
						e.printStackTrace();
					}
					filecount++;
				}
				//System.out.println("File:" + strFile);
				return FileVisitResult.CONTINUE;
			}

			public FileVisitResult postVisitDirectory(Path dir, IOException exc)
					throws IOException
			{

				//getAllDuplicated(dir, res, md5);
//				System.out.println("Dir:" + dir.toAbsolutePath() );
				return FileVisitResult.CONTINUE;
			}

		});
	}
	static int filecount = 0;
//	public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
//		String root = "/Users/";
//		MessageDigest md5 = MessageDigest.getInstance("MD5");
//
//		Map<String, String> res = new HashMap<>();
//		getAllDuplicated(Paths.get(root), res, md5);
//		System.out.println(filecount);
//		//System.out.println(res);
//	}
}
